import { Component, createRef, ReactElement } from 'react';
import { Button, message, Table } from 'antd';
import EditableCell, { editData } from './EditableCell';
import EditableRow, { IformRef } from './EditableFormRow';
import { SortableContainer, SortableHandle } from 'react-sortable-hoc';
import { arrayMoveImmutable } from 'array-move';
import { InbizIcon, uuid } from '@inbiz/utils';
import './index.less';
import { merge } from 'lodash';
import Color from '../Color';
import { validateRules } from 'rc-field-form/lib/utils/validateUtil';
import { MenuOutlined } from '@ant-design/icons';
import { getMessage } from '@inbiz/react';
type TableProps = Parameters<typeof Table>[0];

interface EditableTableState {
  dataSource: any[];
  columns: (ColumnTypes[number] & {
    editData?: editData;
    dataIndex: string;
    isIdentical?: boolean;
    isIdenticalTitle?: string;
  })[];
}

type ColumnTypes = Exclude<TableProps['columns'], undefined>;

type editableRef = {
  getData: () => Promise<object[]>;
};
type EditableTableProps = Omit<TableProps, 'ref' | 'title'> & {
  title?: string | ReactElement;
  titlePosition?: 'before' | 'after';
  dataSource: object[];
  onTableValueChange?: (value: any) => void;
  disableDraggable?: boolean;
  columns: (ColumnTypes[number] & {
    editData?: editData;
    dataIndex: string;
    isIdentical?: boolean;
  })[];
  showAdd?: boolean;
  addText?: string;
  onAdd?: (record: object[]) => object | void;
  isRowSelection?: boolean;
  addBtn:
    | boolean
    | {
        children: string;
        className?: string;
        type?: 'primary' | 'ghost' | 'dashed' | 'link' | 'text' | 'default';
        icon?: ReactElement;
        [key: string]: any;
      };
  deleteBtn:
    | boolean
    | {
        children: string;
        className?: string;
        type?: 'primary' | 'ghost' | 'dashed' | 'link' | 'text' | 'default';
        icon?: ReactElement;
        danger?: boolean;
        [key: string]: any;
      };
  ref?: any;
  maxLength?: number;
  adddisabled?: boolean;
  disabled?: boolean;
};

const computeScroll = (dataList) =>
  dataList.reduce((prev, next) => {
    const sum = prev + (next.width || 200);
    return sum;
  }, 0);

const Sortable = SortableContainer((props: any) => <tbody {...props} />);

const DragHandle = SortableHandle(() => (
  <MenuOutlined style={{ cursor: 'grab', color: '#999' }} />
));

class EditableTable extends Component<EditableTableProps, EditableTableState> {
  static defaultProps: {
    bordered: boolean;
    size: string;
    pagination: boolean;
    dataSource: any[];
    addBtn: boolean;
    deleteBtn: boolean;
    titlePosition: 'after' | 'before';
    isRowSelection?: boolean;
  };
  protected checkedKeys: string[] = [];
  protected formRef: { [key: string]: IformRef } = {};

  constructor(props: EditableTableProps) {
    super(props);
    this.state = {
      dataSource: (props.dataSource || []).map((item) => {
        const newItem = { ...item };
        const rowKey =
          typeof props.rowKey === 'function'
            ? props.rowKey(item)
            : props.rowKey || 'id';
        if (!newItem[rowKey] && newItem[rowKey] !== 0) {
          newItem[rowKey] = uuid(0, 15);
        }
        return newItem;
      }),
      columns: this.columnsFormat(props.columns, props.disableDraggable),
    };
    if (
      this.props.maxLength &&
      this.state.dataSource.length > this.props.maxLength
    ) {
      this.state.dataSource.length = this.props.maxLength;
    }
  }

  columnsFormat = (
    columns: EditableTableProps['columns'],
    disableDrag: EditableTableProps['disableDraggable'],
  ) => {
    let newColumns = (columns || []).map(
      (col: EditableTableProps['columns'][number]) => {
        if (!col.editData) {
          return col;
        }
        // 验证输入空格不通过
        let editData = col.editData;
        if (editData?.rules) {
          const requiredData = editData.rules.find((item) => item.required);
          const validator = editData.rules.find((item) => item.validator);
          if (requiredData?.required && !validator) {
            editData?.rules.push({
              validator: (rule, value: string) => {
                if (typeof value === 'string') {
                  return value.length === 0 || value.trim().length
                    ? Promise.resolve()
                    : Promise.reject();
                } else {
                  return Promise.resolve();
                }
              },
              message:
                requiredData.message ||
                getMessage('EditableTable.thisFieldCannotBeEmpty'),
            });
          }
        }

        const renderMap = RenderMap(col);
        return {
          render:
            typeof col.editData?.type === 'string'
              ? renderMap[col.editData?.type]
              : null,
          ...col,
          onCell: (record: any, rowIndex) => ({
            record,
            dataIndex: col.dataIndex,
            rowIndex: rowIndex,
            title: col.title,
            editData,
            handleSave: this.handleSave,
          }),
        };
      },
    ) as EditableTableProps['columns'];
    if (
      !disableDrag &&
      newColumns[newColumns.length - 1].title !=
        getMessage('EditableTable.sort')
    ) {
      newColumns.push({
        title: getMessage('EditableTable.sort'),
        dataIndex: 'sort',
        width: 50,
        className: 'drag-visible',
        fixed: 'right',
        render: () => <DragHandle />,
      });
    }
    return newColumns;
  };
  /*** 对外暴露的接口 */
  getData = async (errorMsg: boolean) => {
    await this.valid(errorMsg);
    return this.state.dataSource;
  };
  valid = async (errorMsg: boolean) => {
    await Promise.all(
      Object.values(this.formRef).map((item) => item.validateFields()),
    ).catch((e) => {
      return Promise.reject({
        type: 'inputIng', // 还在输入中
      });
    });

    if (
      this.props.maxLength &&
      this.state.dataSource.length > this.props.maxLength
    ) {
      await Promise.reject({
        type: 'maxLength',
        value: this.props.maxLength,
        msg: `表格数据超过最大限制"${this.props.maxLength}条"`,
      });
    }

    const { columns, dataSource } = this.state;
    const validColumns = columns.filter((item) => item?.editData?.rules);

    if (dataSource?.length && validColumns.length) {
      for (const data of dataSource) {
        for (const column of validColumns) {
          await validateRules(
            [column['dataIndex']],
            data[column['dataIndex']],
            column.editData?.rules || [],
            {},
            true,
          ).catch((e) => {
            const ref = this.formRef[data[this.getKey()]];
            ref.refs[column['dataIndex']]?.current?.startEdit();
            ref.validateFields();
            return Promise.reject(e);
          });
        }
      }
    }
    const columnsIdenticalList = (
      this.props.columns as {
        dataIndex: string;
        title: string;
        isIdentical?: boolean;
        isIdenticalTitle?: string;
      }[]
    ).filter((item) => item.isIdentical);
    if (columnsIdenticalList?.length) {
      //同列字段重名效验
      let titleMsg;
      const isIdenticalResult = columnsIdenticalList.every((item) => {
        const { dataIndex, title } = item;
        const list = this.state.dataSource
          .map((item) => item[dataIndex])
          .filter((item) => item !== '' && item !== undefined && item !== null);
        // 当字段为fieldName（字段名）时，将它的别名考虑进来判断重名
        if (dataIndex === 'fieldName') {
          const nowModalArr: string[] = []; // 当前所有涉及的模型（一对多的时候）
          const allModalNameArr: object = {}; // 存储某个模型对应行的名称（有别名存别名，无别名，存字段名），用于判断重复
          list.map((item, index) => {
            const nowModal = item?.split('.');
            if (nowModalArr.indexOf(nowModal[0]) === -1) {
              // 初始化
              allModalNameArr[nowModal[0]] = [];

              // 添加数据
              nowModalArr.push(nowModal[0]);
              this.state.dataSource[index].anotherName
                ? allModalNameArr[nowModal[0]].push(
                    this.state.dataSource[index].anotherName,
                  )
                : allModalNameArr[nowModal[0]].push(nowModal[1]);
            } else {
              this.state.dataSource[index].anotherName
                ? allModalNameArr[nowModal[0]].push(
                    this.state.dataSource[index].anotherName,
                  )
                : allModalNameArr[nowModal[0]].push(nowModal[1]);
            }
          });
          // 为了提示更加精确，精确到别名列
          const anotherNameList = this.state.dataSource
            .filter((item) => item.anotherName)
            .map((item) => item.anotherName);
          if (anotherNameList.length !== [...new Set(anotherNameList)].length) {
            titleMsg = getMessage('EditableTable.alias');
            return false;
          }

          // 判断对应模型名称是否重复
          const flag =
            Object.keys(allModalNameArr).filter((item) => {
              return (
                allModalNameArr[item].length !==
                [...new Set(allModalNameArr[item])].length
              );
            })?.length === 0;
          titleMsg = getMessage('EditableTable.aliasesAnd') + title;
          return flag;
        }
        if (list.length !== [...new Set(list)].length) {
          titleMsg = item.isIdenticalTitle
            ? item.isIdenticalTitle
            : dataIndex === 'operationflag'
            ? getMessage('EditableTable.identification')
            : title;
          return false;
        } else {
          return true;
        }
      });
      if (!isIdenticalResult) {
        if (errorMsg !== false) {
          message.error(
            <span>
              {titleMsg}
              {getMessage('EditableTable.cannotRepeat')}!
            </span>,
          );
        }
        await Promise.reject({
          type: 'identical',
          msg: `${titleMsg}${getMessage('EditableTable.cannotRepeat')}!`,
        });
      }
    }
  };
  // 外部更新数据
  updateSource = (id: string, data: object) => {
    const index = this.state.dataSource.findIndex(
      (item) => item[this.getKey()] === id,
    );
    if (index !== -1) {
      const newData = [...this.state.dataSource];
      newData[index] = Object.assign({}, this.state.dataSource[index], data);
      this.setState({
        dataSource: newData,
      });
      return true;
    } else {
      return false;
    }
  };
  // 获取所有内部数据， 不校验
  getDataSource = () => {
    return this.state.dataSource;
  };
  // 更新所有数据
  upData = (dataSource: object[]) => {
    this.setState({
      dataSource,
    });
  };
  /*** 对外暴露的接口 */

  componentDidUpdate(prevProps: EditableTableProps) {
    if (prevProps.dataSource !== this.props.dataSource) {
      this.setState({ dataSource: this.props.dataSource });
    }
    if (prevProps.columns !== this.props.columns) {
      this.setState({
        columns: this.columnsFormat(
          this.props.columns,
          this.props.disableDraggable,
        ),
      });
    }
  }

  onSortEnd = ({
    oldIndex,
    newIndex,
  }: {
    oldIndex: number;
    newIndex: number;
  }) => {
    const { dataSource } = this.state;
    if (oldIndex !== newIndex) {
      const newData = arrayMoveImmutable(
        [...dataSource].slice(),
        oldIndex,
        newIndex,
      ).filter((el) => !!el);
      this.setState({ dataSource: newData });
    }
  };

  DraggableContainer = (props: any) => {
    const sortableRef: any = createRef();
    return (
      <Sortable
        useDragHandle
        pressDelay={100}
        helperClass="row-dragging"
        onSortEnd={this.onSortEnd}
        helperContainer={() => {
          return (
            sortableRef.current.scrollContainer ||
            document.body
          );
        }}
        ref={sortableRef}
        {...props}
      />
    );
  };

  handleDelete = () => {
    if (!this.checkedKeys.length) {
      message.warning(getMessage('EditableTable.pleaseSelectTheRowToDelete'));
    } else {
      const newData = this.state.dataSource.filter(
        (item) => !this.checkedKeys.includes(item[this.getKey()]),
      );
      this.setState({
        dataSource: newData,
      });
      this.props?.onTableValueChange?.(newData);
      this.checkedKeys = [];
      this.handleCheckChange([], []);
    }
  };

  handleAdd = () => {
    const { dataSource } = this.state;
    const newData = this.props.onAdd?.(dataSource);
    if (newData) {
      this.setState({
        dataSource: [...dataSource, newData],
      });
    }
  };
  handleSave = (row: any) => {
    const newData = [...this.state.dataSource];
    const key = this.getKey(row);
    for (let i = 0; i < newData.length; i++) {
      if (newData[i][key] === row[key]) {
        newData[i] = {
          ...newData[i],
          ...row,
        };
        break;
      }
      // 如果存在children 则继续查找
      if (newData[i]?.children && newData[i]?.children.length > 0) {
        const index = newData[i].children.findIndex(
          (child: object) => child[key] === row[key],
        );
        if (index !== -1) {
          newData[i].children[index] = {
            ...newData[i].children[index],
            ...row,
          };
          break;
        }
      }
    }
    this.props.onTableValueChange?.(newData);
    this.setState({ dataSource: newData });
  };

  getKey = (row?: any): string => {
    let key = this.props.rowKey || 'key';
    if (typeof key === 'function') {
      key = key(row || this.state.dataSource[0]) as string;
    }
    return key;
  };
  getTitle = () => {
    if (this.props.title || this.props.addBtn || this.props.deleteBtn) {
      const addBtn =
        this.props.addBtn &&
        (typeof this.props.addBtn === 'boolean'
          ? {
              children: getMessage('EditableTable.add'),
              type: 'primary' as const,
            }
          : this.props.addBtn);
      const deleteBtn =
        this.props.deleteBtn &&
        (typeof this.props.deleteBtn === 'boolean'
          ? { children: getMessage('EditableTable.delete'), danger: true }
          : this.props.deleteBtn);
      return (
        <>
          {this.props.title &&
            this.props.titlePosition === 'before' &&
            this.props.title}
          {addBtn && (
            <Button
              {...addBtn}
              disabled={
                this.props.adddisabled || this.props.disabled ? true : undefined
              }
              onClick={this.handleAdd}
            />
          )}
          {deleteBtn && (
            <Button
              {...deleteBtn}
              disabled={!(this.checkedKeys.length > 0) || this.props.disabled}
              onClick={this.handleDelete}
            />
          )}
          {this.props.title &&
            this.props.titlePosition === 'after' &&
            this.props.title}
        </>
      );
    } else {
      return undefined;
    }
  };
  handleCheckChange = (keys: string[], rows: object[]) => {
    this.checkedKeys = keys;
    this.props.rowSelection?.onChange?.(keys, rows);
  };

  render() {
    const { dataSource, columns } = this.state;
    const { components } = this.props;
    const rowKey = this.getKey(dataSource[0]);
    this.formRef = {};
    const _components = merge(
      {
        body: {
          wrapper: this.DraggableContainer,
          row: EditableRow(
            dataSource,
            rowKey,
            this.formRef as unknown as IformRef,
            this.props.disableDraggable,
          ),
          cell: EditableCell,
        },
      },
      components || {},
    );
    const rowSelection = Object.assign(
      {
        type: 'checkbox',
        fixed: true,
        disabled: this.props.disabled,
        // 禁用多选框，查看状态时，不允许选中
        getCheckboxProps: () => ({
          disabled: this.props.disabled,
        }),
      },
      this.props.rowSelection,
      { onChange: this.handleCheckChange },
    );
    return (
      <div
        className={`editableTable ${
          this.props.scroll?.y && !this.props.disableDraggable
            ? 'bodyScorll'
            : ''
        }`}
      >
        <Table
          {...this.props}
          size={'small'}
          rowSelection={this.props.isRowSelection ? rowSelection : undefined}
          components={_components}
          dataSource={dataSource}
          columns={columns as ColumnTypes}
          title={
            this.props.title || this.props.addBtn || this.props.deleteBtn
              ? this.getTitle
              : undefined
          }
          scroll={{
            x: computeScroll(columns),
            y: this.props.scroll?.y ? this.props.scroll?.y : undefined,
          }}
          className={`editable-table ${this.props.className || ''}`}
        />
      </div>
    );
  }
}

EditableTable.defaultProps = {
  titlePosition: 'after',
  bordered: true,
  size: 'middle',
  pagination: false,
  dataSource: [],
  addBtn: true,
  deleteBtn: true,
  isRowSelection: true,
};
type IColumns = EditableTableProps['columns'];
export default EditableTable;
export type { editableRef, IColumns };

const RenderMap = (col: { editData?: { options?: any[] } }) => {
  return {
    Color: (value: string) => <Color value={value} />,
    IconPicker: (value: any) => {
      return value ? <InbizIcon type={value?.type} /> : '';
    },
    Select: (value: any) => {
      return (
        col.editData?.options?.find?.((item) => item.value === value)?.label ||
        value
      );
    },
    AutoComplete: (value: string) =>
      col.editData?.options?.find((item) => item.value === value)?.value ||
      value,
    MultilingualInput: (value: string) => getMessage(value),
    PageSelect: (value?: string) => getMessage(value?.split(',')[2] || ''),
    ApiQuery: (value?: object) => {
      if (value) {
        return (
          <a href="javascript:;" style={{ color: 'var(--ant-primary-color)' }}>
            查看详情
          </a>
        );
      } else {
        return undefined;
      }
    },
  };
};
